#ifndef __GLF_CORE_LAZY_H_INCLUDED__
#define __GLF_CORE_LAZY_H_INCLUDED__

#include <glf/core/alignedStaticArray.h>

namespace glf {

//! Lazy construct / destroy an object.
/**
 *  Some algorithms are not friendly on object construction / destruction. Such algorithms
 *  requires to allocate memory for such object and then use inplace new / inplace delete
 *  to manage the object. The lazy class simplify this process.
 */
template<typename T>
class Lazy
{

public:

	typedef T                ValueType;
	typedef ValueType&       Reference;
	typedef const ValueType& ConstReference;

	//! Construct (or more accuratly, don't construct) the object.
	Lazy()
		GLF_DEBUG_ONLY(
			: mIsInitialized(false)
		)
	{
	}

	//! Desttroy (or more accuratly, don't destroy) the object.
	~Lazy()
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(!mIsInitialized); // Lazy variable was not destroyed correctly.
		)
	}

	//! Default constructor for the lazy object.
	inline void Construct()
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(!mIsInitialized); // Lazy variable was already constructed.
			mIsInitialized = true;
		)

		new(mValue.p) ValueType;
	}

	//! Single parameter overloaded constructor for the lazy object.
	template<typename U>
	inline void Construct(const U& other)
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(!mIsInitialized); // Lazy variable was already constructed.
			mIsInitialized = true;
		)

		new(mValue.p) ValueType(other);
	}

	//! Destructor for the lazy object.
	inline void Destroy()
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(mIsInitialized); // Lazy variable was already destroyed.
			mIsInitialized = false;
		)

		((ValueType*)mValue.p)->~ValueType();
	}

	//! Read
	inline operator Reference()
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(mIsInitialized); // Lazy variable is not constructed.
		)

		return *((ValueType*)mValue.p);
	}

	//! Read
	inline operator ConstReference() const
	{
		GLF_DEBUG_ONLY(
			GLF_ASSERT(mIsInitialized); // Lazy variable is not constructed.
		)

		return *((ValueType*)mValue.p);
	}

private:

	// The object is an array of char of size sizeof(ValueType) and aligned to
	// alignof(ValueType). Since we're gonna use placement new on mValue,
	// it is mandatory that it's aligned correctly.
	AlignedStaticArray<char, sizeof(ValueType), GLF_ALIGNOF(ValueType)> mValue;

	// Debug information for tracking error such as double construct / destruct and
	// accessing the variable before constructing it.
	GLF_DEBUG_ONLY(
		bool mIsInitialized;
	)
};

} // end namespace glf

#endif // __GLF_CORE_LAZY_H_INCLUDED__

